﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

public struct int4
{
    public int x, y, z, w;

    public int4(int x, int y, int z, int w)
    {
        this.x = x;
        this.y = y;
        this.z = z;
        this.w = w;
    }

    public static int4 Zero
    {
        get { return new int4(0, 0, 0, 0); }
    }
}

namespace LevelEditor
{
    public struct WeightContainer
    {
        float w0;
        float w1;
        float w2;
        float w3;
        float w4;
        float w5;
        float w6;
        float w7;

        public float GetFloat(int i)
        {
            switch (i)
            {
                case 0:
                    return w0;
                case 1:
                    return w1;
                case 2:
                    return w2;
                case 3:
                    return w3;
                case 4:
                    return w4;
                case 5:
                    return w5;
                case 6:
                    return w6;
                case 7:
                    return w7;
                default:
                    throw new IndexOutOfRangeException();
            }
        }

        public void SetFloat(int i, float val)
        {
            switch (i)
            {
                case 0:
                    w0 = val;
                    break;
                case 1:
                    w1 = val;
                    break;
                case 2:
                    w2 = val;
                    break;
                case 3:
                    w3 = val;
                    break;
                case 4:
                    w4 = val;
                    break;
                case 5:
                    w5 = val;
                    break;
                case 6:
                    w6 = val;
                    break;
                case 7:
                    w7 = val;
                    break;
                default:
                    throw new IndexOutOfRangeException();
            }
        }

        public float this[int i]
        {
            get { return GetFloat(i); }

            set { SetFloat(i, value); }
        }

        public WeightContainer(float[] weights)
        {
            w0 = weights[0];
            w1 = weights[1];
            w2 = weights[2];
            w3 = weights[3];
            w4 = weights[4];
            w5 = weights[5];
            w6 = weights[6];
            w7 = weights[7];
        }

        public int Length
        {
            get { return 8; }
        }
    }


    public struct VertexTextureMap : IVertexType
    {
        public Vector3 Position;
        public Vector3 Normal;
        public Vector2 uv;
        public WeightContainer terrainWeights;

        static VertexElement[] elements = new VertexElement[]
        {
            new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
            new VertexElement(12, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
            new VertexElement(24, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0),
            new VertexElement(32, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 1),
            new VertexElement(48, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 2),
        };

        public VertexTextureMap(Vector3 position, Vector3 normal, Vector2 uv, float[] terrainWeights)
        {
            this.Position = position;
            this.Normal = normal;
            this.uv = uv;
            this.terrainWeights = new WeightContainer(terrainWeights);       
        }

        VertexDeclaration IVertexType.VertexDeclaration
        {
            get { return new VertexDeclaration(elements); }
        }


        /// <summary>
        /// Indicates if this vertex follows a terrain rule
        /// </summary>
        /// <param name="rule">The rule to test</param>
        /// <returns>boolean value idincating if this vertex follows the provided rule</returns>
        public bool FollowsRule(TerrainRule rule)
        {
            switch (rule.type)
            {
                case RuleType.AboveAltitude:
                    return Position.Y > ((float)rule.Value);
                case RuleType.BelowAltitude:
                    return Position.Y < ((float)rule.Value);
                case RuleType.AltitudeRange:
                    return Position.Y > ((Vector2)rule.Value).X && Position.Y < ((Vector2)rule.Value).Y;
                case RuleType.Underwater:
                    return false;
                case RuleType.FacesDirection:
                    {
                        Vector4 value = (Vector4)rule.Value;
                        Vector3 dir = new Vector3(value.X, value.Y, value.Z);
                        dir.Normalize();
                        float dot = MathHelper.Clamp(Vector3.Dot(Normal, dir), 0.0f, 1.0f);
                        float angle = MathHelper.ToDegrees((float)Math.Acos(dot));

                        if (angle < value.W)
                            return true;

                        return false;
                    }
                    
                default:
                    return false;
            }
        }
    }
}